package org.xyz.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.xyz.common.Result;
import org.xyz.dao.CodeMapper;
import org.xyz.dao.ProductMapper;
import org.xyz.dao.SettingsMapper;
import org.xyz.dao.UserMapper;
import org.xyz.entity.*;
import org.xyz.enums.SettingsEnum;
import org.xyz.service.WxPayService;
import org.xyz.util.PhoneUtil;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static org.xyz.common.Result.ResultEnum.*;

//https://www.cnblogs.com/bJydsJ/p/15315412.html
// https://www.google.com/search?q=cn&oq=cn&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIGCAEQRRg8MgYIAhBFGDwyBggDEEUYPDIGCAQQRRg8MgYIBRBFGDwyBggGEEUYQTIGCAcQRRhB0gEHNjUyajBqNKgCALACAA&sourceid=chrome&ie=UTF-8
@Tag(name = "控制器负责所有接口", description = "all endpoints come here")
@RequestMapping("/api/")
@RestController
@CrossOrigin(origins = "*")
public class GodController {
    @Autowired
    UserMapper userMapper;
    @Autowired
    CodeMapper codeMapper;
    @Autowired
    SettingsMapper settingsMapper;
    @Autowired
    ProductMapper productMapper;
    @Autowired
    WxPayService wxPayService;

    /****废弃****/
    @ApiOperation(value = "微信预支付", notes = "微信预支付")
    @PostMapping("/createOrder")
    public Result<WxPayRespVO> createOrder(@RequestBody @Validated WXPayOrderReqVO req) throws Exception {
        return Result.success(wxPayService.createOrder(req));
    }

    @ApiOperation(value = "微信支付回调", notes = "微信支付回调")
    @PostMapping("/payNotify")
    public Result payNotify(HttpServletRequest request){
        wxPayService.payNotify(request);//注意：回调接口需要暴露到公网上，且要放开token验证
        return Result.success();
    }

    @ApiOperation(value = "微信支付结果查询", notes = "微信支付查询")
    @PostMapping("/checkOrder")
    public Result checkOrder(@RequestBody PayQueryVO payQueryVO){
        User user = wxPayService.queryPayOrder(payQueryVO.getTxNO());
        if (user != null) {
            return Result.success(user);
        }
        return Result.error(-200, "支付失败或取消");
    }

    @GetMapping("/test")
    public String test() {
        Product product = Product.builder().type(12).price(102).createTime("2024-05-12").build();
        productMapper.insert(product);
        return "hello, spring";
    }

    @GetMapping("/user/usecode")
    public Result<Void> useInviteCode(@RequestParam String code, @RequestParam String phone) {
        User selectedUser = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (selectedUser == null) {
            return Result.error(Result.ResultEnum.USER_IS_EXISTS.getCode(), Result.ResultEnum.USER_IS_EXISTS.getMsg());
        }
        Settings settings = settingsMapper.selectOne(new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, "days_token"));
        int daysByCode = 0;
        if (settings != null) {
            daysByCode = Integer.parseInt(settings.getValue());
        }
        Code theCode = codeMapper.selectOne(new LambdaQueryWrapper<Code>().eq(Code::getToken, code));
        if (theCode != null) {
            if (theCode.getUsed()==1) {
                return Result.error(TOKEN_HAS_USED.getCode(), TOKEN_HAS_USED.getMsg());
            }
            theCode.setUsed(1);

            int result = codeMapper.updateById(theCode);
            if (result > 0) {
               // int remained = selectedUser.getLeftTrials();
               // selectedUser.setLeftTrials(remained + daysByCode);
                Integer leftDays = selectedUser.getLeftDays();
                selectedUser.setLeftDays(leftDays + daysByCode);
                userMapper.updateById(selectedUser);
            }
            return Result.success("兑换成功");
        }
        return Result.error(TOKEN_NOT_EXIST.getCode(), TOKEN_NOT_EXIST.getMsg());
    }

    @PostMapping("/user/consume")
    public Result<Void> userConsume(@RequestBody @Validated ConsumeVO consumeVO) {
        // 支付隐藏阶段不收费
        /*LambdaQueryWrapper<Settings> settingQueryWrapper = new LambdaQueryWrapper<>();
        settingQueryWrapper.eq(Settings::getSetting, "admin_pwd");
        Settings settings = settingsMapper.selectOne(settingQueryWrapper);
        if (settings!=null && "0".equals(settings.getValue())) {
            return Result.success();
        }*/

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(consumeVO.getPhone() != null, User::getPhone, consumeVO.getPhone());
        User selectedUser = userMapper.selectOne(queryWrapper);
        if (selectedUser != null) {
            Integer leftDays = selectedUser.getLeftDays();
            if (leftDays==null || leftDays<1) {
                Integer leftTimes = selectedUser.getLeftTrials();
                if (leftTimes!=null && leftTimes>0) {
                    selectedUser.setLeftTrials(leftTimes-1);
                    userMapper.updateById(selectedUser);
                    return Result.success();
                } else {
                    // 提示用户已经没有使用次数，去购买
                    return Result.error(Result.ResultEnum.TIME_IS_NULL.getCode(), Result.ResultEnum.TIME_IS_NULL.getMsg());
                }
            }
            return Result.success();
        }

        return Result.error(Result.ResultEnum.USER_IS_EXISTS.getCode(), Result.ResultEnum.USER_NOT_EXIST.getMsg());
    }

    @PostMapping("/user/login")
    public Result<JSONObject> userLogin(@RequestBody @Validated LoginVO loginVO) {
        String code = loginVO.getCode();
        JSONObject phoneNumber = PhoneUtil.getPhoneNumber(code);
//        String phone = JSONUtil.toJsonStr(phoneNumber);
        return Result.success(phoneNumber);
    }

    /** 用户注册 */
    @PostMapping("/user/insert")
    public Result<User> userRegister(@RequestBody @Validated User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getPhone() != null, User::getPhone, user.getPhone());
        User selectedUser = userMapper.selectOne(queryWrapper);
        // 新用户
        if (selectedUser == null) {
            // 可免费使用次数
            LambdaQueryWrapper<Settings> eq = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.TRIALS_USER.getField());
            Settings settings = settingsMapper.selectOne(eq);
            Integer trials = Integer.parseInt(settings.getValue());
           // user.setLeftTrials(trials);
            user.setLeftTrials(0);
            user.setLeftDays(trials);
            user.setCreateTime(DateUtil.now());
            userMapper.insert(user);
            return Result.success(user);
        }
        return Result.success(selectedUser);
    }

    /** 查询数据 */
    @GetMapping("/user/get")
    public Result<User> show(@RequestParam String phone) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(phone != null, User::getPhone, phone);
        User selectedUser = userMapper.selectOne(queryWrapper);
        return Result.success(selectedUser);
    }

    @GetMapping("/user/list")
    public Result<User> listUsers(@RequestParam Integer currPage){
        int pageSize = 50;
        IPage page = new Page(1, pageSize);
        IPage selectPages = userMapper.selectPage(page, null);
        return Result.success(selectPages.getRecords());
    }

    @GetMapping("/settings/get")
    public Result<Settings> querySettingsTrials() {
        List<Map<String, Object>> maps = settingsMapper.selectMaps(null);
        Map<String, String> myHashMap = new HashMap<>();
        maps.forEach(map->{ myHashMap.put((String)map.get("setting"), (String)map.get("value")); });
        maps.toArray();
        return Result.success(myHashMap);
    }

    @PostMapping("/settings/token")
    public Result<Void> setTokenSettings(@RequestBody String daysJson) {
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(daysJson);
        String tokenDays = jsonObject.getStr("token_days");

        LambdaQueryWrapper<Settings> tokenDaysQW = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.DAYS_TOKEN.getField());

        Settings tokenDaysSettings = settingsMapper.selectOne(tokenDaysQW); tokenDaysSettings.setValue(tokenDays);

        settingsMapper.update(tokenDaysSettings, tokenDaysQW);

        return Result.success();
    }

    @PostMapping("/settings/trials")
    public Result<Void> setTrialsSettings(@RequestBody String timesJson) {
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(timesJson);
        String trialDays = jsonObject.getStr("trial_days");

        LambdaQueryWrapper<Settings> trialsDaysQW = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.TRIALS_USER.getField());

        Settings trialDaysSettings = settingsMapper.selectOne(trialsDaysQW); trialDaysSettings.setValue(trialDays);

        settingsMapper.update(trialDaysSettings, trialsDaysQW);

        return Result.success();
    }

    /**
     * 修改管理员密码
     * @param pwdJson
     * @return
     */
    @PostMapping("/settings/updateAdminPwd")
    public Result<Void> updateAdminPwd(@RequestBody String pwdJson){
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(pwdJson);
        String password = jsonObject.getStr("pwd");
        if (StrUtil.isBlank(password)) {
            return Result.error(Result.ResultEnum.PARAM_ERROR.getCode(), PARAM_ERROR.getMsg());
        }

        LambdaUpdateWrapper<Settings> updateWrapper = Wrappers.<Settings>lambdaUpdate();
        updateWrapper.eq(Settings::getSetting, SettingsEnum.ADMIN_PWD.getField());
        updateWrapper.set(Settings::getValue, password);
        this.settingsMapper.update(updateWrapper);
        return Result.success();
    }

    @PostMapping("/settings/wxPayVisibility")
    public Result<Void> setPayVisibilitySettings(@RequestBody String pricesJson) {
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(pricesJson);
        String visibility = jsonObject.getStr("visibility");

        LambdaQueryWrapper<Settings> wxPayVisibility = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.ADMIN_PWD.getField());

        Settings payVisibilitySettings = settingsMapper.selectOne(wxPayVisibility);
        payVisibilitySettings.setValue(visibility);

        settingsMapper.update(payVisibilitySettings, wxPayVisibility);

        return Result.success();
    }

    @PostMapping("/settings/price")
    public Result<Void> setPriceSettings(@RequestBody String pricesJson) {
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(pricesJson);
        String monthPrice = jsonObject.getStr("price_month");
        String yearPrice = jsonObject.getStr("price_year");
        String trialPrice = jsonObject.getStr("price_trial");

        LambdaQueryWrapper<Settings> monthPriceQW = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.PRICE_MONTH.getField());
        LambdaQueryWrapper<Settings> yearPriceQW = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.PRICE_YEAR.getField());
        LambdaQueryWrapper<Settings> trialPriceQW = new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.PRICE_TRIAL.getField());

        Settings monthPriceSettings = settingsMapper.selectOne(monthPriceQW); monthPriceSettings.setValue(monthPrice);
        Settings yearPriceSettings = settingsMapper.selectOne(yearPriceQW); yearPriceSettings.setValue(yearPrice);
        Settings trialPriceSettings = settingsMapper.selectOne(trialPriceQW); trialPriceSettings.setValue(trialPrice);

        settingsMapper.update(monthPriceSettings, monthPriceQW);
        settingsMapper.update(yearPriceSettings, yearPriceQW);
        settingsMapper.update(trialPriceSettings, trialPriceQW);

        return Result.success();
    }

    @GetMapping("/code/list")
    public String downloadCodeList(Model model){
        LambdaQueryWrapper<Code> queryWrapper = new LambdaQueryWrapper<Code>().eq(Code::getUsed, 0);
        List<Code> codeList = codeMapper.selectList(queryWrapper);
        model.addAttribute("code_list", codeList);
        return "codeList";
    }

    @GetMapping("/code/download")
    public void downloadExcel(HttpServletResponse response) throws IOException {
        LambdaQueryWrapper<Code> queryWrapper = new LambdaQueryWrapper<Code>().eq(Code::getUsed, 0);
        List<Code> codeList = codeMapper.selectList(null);

        Settings daysToken = this.settingsMapper.selectOne(new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, SettingsEnum.DAYS_TOKEN.getField()));


        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("Code List");
        Row headerRow = sheet.createRow(0);

        // 创建表头
        headerRow.createCell(0).setCellValue("ID");
        headerRow.createCell(1).setCellValue("兑换码");
        headerRow.createCell(2).setCellValue("创建日期");
        headerRow.createCell(3).setCellValue("兑换状态");
        headerRow.createCell(4).setCellValue("可兑换额度");

        // 填充数据
        int rowNum = 1;
        for (Code code : codeList) {
            Row row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue(code.getId());
            row.createCell(1).setCellValue(code.getToken());
            row.createCell(2).setCellValue(code.getCreateTime());
            row.createCell(3).setCellValue(code.getUsed() == 1? "已兑换":"未兑换");
            row.createCell(4).setCellValue(daysToken.getValue());
        }

        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        String fileName = String.format("兑换码-%s.xlsx", DateUtil.now().replace(' ', '_'));
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8"));

        // 将Excel写入到响应中
        workbook.write(response.getOutputStream());
        workbook.close();
    }

    @GetMapping("/gencode")
    public Result<Integer> generateInviteCode(@RequestParam Integer count){
        for (int i = 0; i < count; i++) {
            String randomString = RandomUtil.randomString(5);
            Code code = Code.builder().token(randomString).used(0).createTime(DateUtil.now()).build();
            codeMapper.insert(code);
        }
        LambdaQueryWrapper<Code> queryWrapper = new LambdaQueryWrapper<Code>().eq(Code::getUsed, 0);
        long total = codeMapper.selectCount(queryWrapper);
        return Result.success(total);
    }

    @GetMapping("/codecount")
    public Result<Integer> generateInviteCode(){
        LambdaQueryWrapper<Code> queryWrapper = new LambdaQueryWrapper<Code>().eq(Code::getUsed, 0);
        long total = codeMapper.selectCount(queryWrapper);
        return Result.success(total);
    }

    /** 修改数据 */
    @PutMapping("/user/update")
    public Object update(Long id, String name) {
        User user = User.builder().build();
        user.setId(id);
        return userMapper.updateById(user);
    }

    /**
     * 验证管理员密码是否正确
     * @param pwdJson
     * @return
     */
    @PostMapping("/admin/verifyAdminPwd")
    public Result<Void> verifyAdminPwd(@RequestBody String pwdJson){
        cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(pwdJson);
        String password = jsonObject.getStr("pwd");
        if (StrUtil.isBlank(password)) {
            return Result.error(Result.ResultEnum.PARAM_ERROR.getCode(), PARAM_ERROR.getMsg());
        }

        LambdaQueryWrapper<Settings> queryWrapper = Wrappers.<Settings>lambdaQuery().eq(Settings::getSetting, SettingsEnum.ADMIN_PWD.getField());

        Settings settings = this.settingsMapper.selectOne(queryWrapper);
        if (StrUtil.equals(settings.getValue(), password)) {
            return Result.success();
        }

        return Result.error(Result.ResultEnum.PASSWORD_ERROR.getCode(), PASSWORD_ERROR.getMsg());
    }

}
